package us.wili.dev.common.util.xinge.v2.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.util.FieldInfo;
import com.alibaba.fastjson.util.TypeUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import us.wili.dev.common.util.EncryptUtil;
import us.wili.dev.common.util.httpclient.HttpClientUtil;
import us.wili.dev.common.util.xinge.v2.XinGeErrorCode;
import us.wili.dev.common.util.xinge.v2.XinGeProperties;
import us.wili.dev.common.util.xinge.v2.XinGeUtilErrorCode;
import us.wili.dev.common.util.xinge.v2.exception.XinGeException;
import us.wili.dev.common.util.xinge.v2.model.BaseRsp;

import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

/**
 * author: lefore
 * date: 2018/8/29
 */
public abstract class AbstractXinGeService {

    private Logger logger = LoggerFactory.getLogger(AbstractXinGeService.class);

    private XinGeProperties xinGeProp;
    private HttpClientUtil httpClientUtil;

    protected AbstractXinGeService(XinGeProperties xinGeProp, HttpClientUtil httpClientUtil) {
        this.xinGeProp = xinGeProp;
        this.httpClientUtil = httpClientUtil;
    }

    public <T, R> R executeGet(T req, String path, TypeReference<BaseRsp<R>> type) {
        String schema = xinGeProp.getIfSecure() ? "https" : "http";
        String host = xinGeProp.getHost();
        String accessId = xinGeProp.getAccessId();
        String secretKey = xinGeProp.getSecretKey();

        Map<String, String> params = tranObjectToMap(req, req.getClass());
        params.put("access_id", accessId);
        params.put("timestamp", new Date().toInstant().getEpochSecond() + "");
        String sign = sign("GET", host, path, params, secretKey);
        params.put("sign", sign);

        URIBuilder uriBuilder = new URIBuilder();
        uriBuilder.setScheme(schema);
        uriBuilder.setHost(host);
        uriBuilder.setPath(path);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            uriBuilder.addParameter(entry.getKey(), entry.getValue());
        }

        URI uri = null;
        try {
            uri = uriBuilder.build();
        } catch (URISyntaxException e) {
            logger.error("构建 uri 失败", e);
        }

        String api = uri.getScheme() + "://" + uri.getHost() + uri.getPath();

        try {
            logger.info("请求 url：{}\n请求信息：{}", api, uri.getQuery());
            HttpClientUtil.HttpResult rsp = httpClientUtil.doGet(uri.toString());
            logger.info("返回结果，url：{}\nHTTP Status：{}", api, rsp.getStatus());

            if (rsp.getStatus() != HttpStatus.SC_OK) {
                throw new XinGeException(XinGeUtilErrorCode.HTTP_STATUS_NOT_OK);
            }

            String body = rsp.getResult();
            logger.info("返回结果，url：{}\n响应：{}", api, body);

            BaseRsp<R> result = JSON.parseObject(body, type);
            if (result.getRetCode() != XinGeErrorCode.SUCCESS.getCode()) {
                throw new XinGeException(result.getRetCode(), result.getErrMsg());
            }
            return result.getResult();
        } catch (XinGeException e) {
            logger.error("XinGeException: [ code=" + e.getCode() + " ,msg=" + e.getMsg() + " ]", e);
            throw e;
        } catch (Exception e) {
            logger.error("XinGeUtil occur Exception", e);
            throw new XinGeException(XinGeUtilErrorCode.ERROR, e);
        }
    }

    /**
     * 签名
     *
     * @param method
     * @param host
     * @param path
     * @param params
     * @param secretKey
     * @return
     */
    private String sign(String method, String host, String path, Map<String, String> params, String secretKey) {
        if (StringUtils.isEmpty(host) || StringUtils.isEmpty(path) || CollectionUtils.isEmpty(params) || StringUtils.isEmpty(secretKey)) {
            return "";
        }

        // 对参数进行排序（将请求参数按参数名做字典序升序排列）
        List<String> list = new ArrayList<>();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            list.add(entry.getKey());
        }
        Collections.sort(list);

        // 拼接请求字符串
        StringBuffer sb = new StringBuffer();
        for (String key : list) {
            sb.append(key + "=" + (null == params.get(key) ? "" : params.get(key)));
        }
        String paramsStr = sb.toString();

        // 拼接签名原文字符串
        String url = method + host + path + paramsStr + secretKey;

        // 生成数字签名
        String sign = EncryptUtil.getMD5(url);

        return sign;
    }

    /**
     * JavaBean 转换为 Map<String,String>，暂不支持 Collection、Map 类型转换
     *
     * @param req
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> Map<String, String> tranObjectToMap(T req, Class<?> clazz) {
        Map<String, String> resultMap = new HashMap<>();
        List<FieldInfo> fieldInfoList = TypeUtils.computeGetters(clazz, null);
        try {
            for (FieldInfo field : fieldInfoList) {
                Object fieldValue = field.method.invoke(req);
                if (fieldValue != null) {
                    resultMap.put(field.name, fieldValue.toString());
                }
            }
            return resultMap;
        } catch (IllegalAccessException e) {
            throw new XinGeException(XinGeUtilErrorCode.ERROR, e);
        } catch (InvocationTargetException e) {
            throw new XinGeException(XinGeUtilErrorCode.ERROR, e);
        }
    }
}
